package com.lantosec.hmrp.modules.workflow.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lantosec.hmrp.core.exception.AppException;
import com.lantosec.hmrp.core.util.DateUtils;
import com.lantosec.hmrp.core.util.FlowableUtils;
import com.lantosec.hmrp.core.util.UUIDUtils;
import com.lantosec.hmrp.modules.notify.service.INotifyInfoService;
import com.lantosec.hmrp.modules.notify.vo.NotifyInfoVO;
import com.lantosec.hmrp.modules.sysdata.entity.SysDataProcess;
import com.lantosec.hmrp.modules.sysdata.entity.SysProcessHasten;
import com.lantosec.hmrp.modules.sysdata.service.ISysDataProcessService;
import com.lantosec.hmrp.modules.sysdata.service.ISysDataProcessTaskService;
import com.lantosec.hmrp.modules.sysdata.service.ISysProcessHastenService;
import com.lantosec.hmrp.modules.system.entity.SysUser;
import com.lantosec.hmrp.modules.system.service.ISysUserService;
import com.lantosec.hmrp.modules.system.service.ISysWorkdayService;
import com.lantosec.hmrp.modules.workflow.enumeration.ProcessVariableEnum;
import com.lantosec.hmrp.modules.workflow.service.IWorkflowRepositoryService;
import com.lantosec.hmrp.modules.workflow.service.IWorkflowRuntimeService;
import com.lantosec.hmrp.modules.workflow.vo.CustomAttributeVO;
import com.lantosec.hmrp.modules.workflow.vo.TaskHastenVO;
import com.lantosec.hmrp.modules.workflow.vo.TaskNoticeVO;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程任务创建监听类
 *
 * @author lichaowei
 * @date 2020/5/26
 */
@Component
@Log4j2
public class UserTaskCreateListener implements TaskListener {
    private static final long serialVersionUID = 1L;

    @Autowired
    private HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    ISysUserService sysUserService;
    @Autowired
    IWorkflowRepositoryService workflowService;
    @Autowired
    ISysWorkdayService workdayService;
    @Autowired
    IWorkflowRuntimeService workflowRuntimeService;
    @Autowired
    private IWorkflowRepositoryService workflowRepositoryService;
    @Autowired
    private ISysDataProcessTaskService dataProcessTaskService;
    @Autowired
    private ISysDataProcessService dataProcessService;
    @Autowired
    private INotifyInfoService notifyInfoService;
    @Autowired
    private ISysProcessHastenService processHastenService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void notify(DelegateTask delegateTask) {
        log.info("流程任务创建监听事件----->taskId:" + delegateTask.getId());

        String userId = (String) delegateTask.getVariable(ProcessVariableEnum.USER_ID.getCode());
        String businessKey = (String) delegateTask.getVariable(ProcessVariableEnum.BUSINESS_KEY.getCode());
        // 获取节点自定义属性对象
        CustomAttributeVO customAttributeVO = workflowRepositoryService.getCustomAttribute(delegateTask.getTaskDefinitionKey()
                , delegateTask.getProcessDefinitionId());
        // 获取节点的操作人列表
        List<String> optUserList = getTaskOptUserList(delegateTask, customAttributeVO.getOperator(), userId);
        // 设置节点的操作/候选人
        setTaskCandidateOrAssignee(delegateTask, optUserList);
        // 设置节点的提醒
        setTaskNotice(delegateTask, customAttributeVO.getNotice(), userId, optUserList);
        // 抄送/只会，只能查看不能编辑
        workflowRuntimeService.setTaskRelay((Task) delegateTask, customAttributeVO.getRelay(), userId, null, Boolean.TRUE);
        // 催办设置
        setTaskHasten(delegateTask, customAttributeVO.getHasten(), userId, optUserList);

        String assignee = CollectionUtils.isNotEmpty(optUserList) ? StringUtils.join(optUserList, ",") : "";
        // 保存任务相关信息
        dataProcessService.updateProcess(delegateTask.getProcessInstanceId(), delegateTask.getId(), delegateTask.getTaskDefinitionKey()
                , delegateTask.getName(), null, null, businessKey);
        dataProcessTaskService.insertTask((Task) delegateTask, assignee, customAttributeVO.getFormLayoutId());

        // 自动流转，如果任务的下一个节点处理人是当前处理人，则自动完成当前任务
        taskAutoComplete(delegateTask, customAttributeVO.getIsAutoComplete(), optUserList, userId, customAttributeVO.getFormLayoutId());
    }

    protected List<String> getTaskOptUserList(DelegateTask task, String operator, String userId) {
        List<String> optUserList = new ArrayList<>();
        if (StringUtils.isNotBlank(operator)) {
            // 表单数据
            JSONObject dataJson = (JSONObject) task.getVariable(ProcessVariableEnum.BUSINESS_DATA.getCode());
            // 根据操作人配置查询用户Id集合
            optUserList = sysUserService.getOperatorUserIdList(operator, dataJson, userId);
        } else {
            // 如果节点没有指定操作人，则查询该节点的历史操作人（用于流程打回用）
            List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .taskDefinitionKey(task.getTaskDefinitionKey())
                    .orderByTaskCreateTime().desc()
                    .list();
            if (CollectionUtils.isNotEmpty(list)) {
                optUserList = list.stream().filter(a -> !Objects.isNull(a.getAssignee()))
                        .map(HistoricTaskInstance::getAssignee).collect(Collectors.toList());
            }
        }
        if (CollectionUtils.isNotEmpty(optUserList)) {
            // 去重
            optUserList = optUserList.stream().distinct().collect(Collectors.toList());
        }

        // 查询任务的当前操作/候选人，如果已经存在，则不重复添加
        /*List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
        for (IdentityLink identityLink : identityLinks) {
            if (!IdentityLinkType.ASSIGNEE.equalsIgnoreCase(identityLink.getType())
                    && !IdentityLinkType.CANDIDATE.equalsIgnoreCase(identityLink.getType())) {
                continue;
            }
            //存在则删除，不再添加
            if (StringUtils.isNotBlank(identityLink.getUserId()) && !optUserList.contains(identityLink.getUserId())) {
                optUserList.add(identityLink.getUserId());
            }
        }*/
        return optUserList;
    }

    /**
     * 设置任务节点操作/候选人
     * @param task              任务信息
     * @param optUserList       操作人配置，保存在流程模型的节点属性上
     */
    private void setTaskCandidateOrAssignee(DelegateTask task, List<String> optUserList) {
        if (CollectionUtils.isNotEmpty(optUserList)) {
            String taskId = task.getId();
            // 去重
            optUserList = optUserList.stream().distinct().collect(Collectors.toList());
            if (optUserList.size() > 1) {
                optUserList.forEach(item -> {
                    taskService.addCandidateUser(taskId, item);
                });
            } else {
                taskService.setAssignee(taskId, optUserList.get(0));
                task.setAssignee(optUserList.get(0));
            }
        } else {
            // 获取流程图
            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
            // 第一个用户任务节点，一般是用户申请节点
            String firstUserTaskId = FlowableUtils.getFirstUserTaskId(bpmnModel);
            // 用户的第一个节点没有指定操作人，不用抛出异常，后面的审核节点都要配置节点操作人
            if(!task.getTaskDefinitionKey().equals(firstUserTaskId)){
                throw new AppException("任务节点" + task.getName() + "的操作人配置无法查询到操作人");
            }
        }
    }

    /**
     * 任务节点通知设置
     * @param task      任务信息
     * @param notice    通知设置，保存在流程模型的节点属性上
     * @param userId    用户Id
     */
    @Transactional(rollbackFor = Exception.class)
    public void setTaskNotice(DelegateTask task, String notice, String userId, List<String> optUserList) {
        if (StringUtils.isBlank(notice)) {
            return;
        }
        TaskNoticeVO taskNoticeVO = JSON.parseObject(notice, TaskNoticeVO.class);
        if (Objects.isNull(taskNoticeVO)) {
            return;
        }
        // 查询用户
        SysUser user = sysUserService.getById(userId);
        if (Objects.isNull(user)) {
            return;
        }
        SysDataProcess dbDataProcess = dataProcessService.getBaseMapper().selectOne(new QueryWrapper<SysDataProcess>()
                .eq(String.valueOf(SysDataProcess.ColName.pro_ins_id), task.getProcessInstanceId()));

        List<NotifyInfoVO> infoVOList = new ArrayList<>();
        // 短信通知
        if (taskNoticeVO.getIsSmsNotice() > 0) {
            // 发送通知
            String content = user.getName() + "您好，流程[" + dbDataProcess.getProInsName() + "]等待您的处理";
            optUserList.forEach(item -> {
                NotifyInfoVO notifyInfoVO = new NotifyInfoVO();
                notifyInfoVO.setNotifyId(UUIDUtils.get());
                notifyInfoVO.setNotifyTypeId("001");
                notifyInfoVO.setTitle("流程任务审核");
                notifyInfoVO.setDescription("流程任务审核");
                notifyInfoVO.setContent(content);
                notifyInfoVO.setSmsTag(1);
                notifyInfoVO.setPublicTag(0);
                notifyInfoVO.setScheduleTag(0);
                notifyInfoVO.setSenderId(userId);
                notifyInfoVO.setSender(user.getName());
                notifyInfoVO.setSendTime(new Date());
                notifyInfoVO.setRecipientId(item);
                infoVOList.add(notifyInfoVO);
            });
        }
        // 到期通知
        if (taskNoticeVO.getIsDueNotice() > 0) {
            Integer dueDays = taskNoticeVO.getDueDays();
            // 到期通知方式(短信)，提供一个查询到期任务的接口（WorkQueryService.queryDueTask(Data data)）
            Date dueData = workdayService.getWorkDay(DateUtils.nowDate(), dueDays);
            taskService.setDueDate(task.getId(), dueData);

            Date sendTime = DateUtils.subtractMinute(dueData,120);
            // 发送通知
            String content = user.getName() + "您好，流程[" + dbDataProcess.getProInsName() + "]将在2个小时以后过期，请尽快处理";
            optUserList.forEach(item -> {
                NotifyInfoVO notifyInfoVO = new NotifyInfoVO();
                notifyInfoVO.setNotifyId(UUIDUtils.get());
                notifyInfoVO.setNotifyTypeId("001");
                notifyInfoVO.setTitle("流程任务审核");
                notifyInfoVO.setDescription("流程任务审核");
                notifyInfoVO.setContent(content);
                notifyInfoVO.setSmsTag(1);
                notifyInfoVO.setPublicTag(0);
                notifyInfoVO.setScheduleTag(1);
                notifyInfoVO.setSendTime(sendTime);
                notifyInfoVO.setSenderId(userId);
                notifyInfoVO.setSender(user.getName());
                notifyInfoVO.setSendTime(new Date());
                notifyInfoVO.setRecipientId(item);
                infoVOList.add(notifyInfoVO);
            });
        }

        // 调用发送通知接口
        infoVOList.forEach(item -> {
            notifyInfoService.noticeSending(item);
        });

    }

    /**
     * 任务自动流转
     * @param delegateTask      任务
     * @param isAutoComplete    是否开启自动流转
     * @param optUserList       任务操作人/候选人用户Id列表
     * @param userId            当前操作人用户Id
     */
    protected void taskAutoComplete(DelegateTask delegateTask, String isAutoComplete, List<String> optUserList, String userId,String layoutId) {
        if ("1".equals(isAutoComplete) && CollectionUtils.isNotEmpty(optUserList) && optUserList.contains(userId)) {
            String taskId = delegateTask.getId();
            List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);
            for (IdentityLink identityLink : identityLinks) {
                //存在则删除，不再添加
                if (StringUtils.isNotBlank(identityLink.getUserId()) && identityLink.getUserId().equals(userId)) {
                    taskService.setAssignee(taskId, userId);
                    taskService.complete(taskId);

                    dataProcessTaskService.saveTask((Task) delegateTask, delegateTask.getAssignee(), layoutId, 1, "自动审核通过", null);
                    return;
                }
            }
        }
    }

    /**
     * 任务节点催办设置
     * @param task      任务信息
     * @param hasten    催办设置，保存在流程模型的节点属性上
     * @param userId    用户Id
     */
    public void setTaskHasten(DelegateTask task, String hasten, String userId, List<String> optUserList) {
        if (StringUtils.isBlank(hasten)) {
            return;
        }
        TaskHastenVO taskHastenVO = JSON.parseObject(hasten, TaskHastenVO.class);
        if (Objects.isNull(taskHastenVO)) {
            return;
        }
        if (!Objects.isNull(taskHastenVO.getIsHasten()) && taskHastenVO.getIsHasten() == 0) {
            return;
        }

        Date startTime = new Date();
        if (!StringUtils.isEmpty(taskHastenVO.getTimeField())) {
            // 表单数据
            JSONObject dataJson = (JSONObject) task.getVariable(ProcessVariableEnum.BUSINESS_DATA.getCode());
            Object timeField = dataJson.get(taskHastenVO.getTimeField());
            if (Objects.isNull(timeField)) {
                return;
            }
            startTime = DateUtils.strToDate(timeField.toString(), DateUtils.DEFAULTFORMAT);
        }
        Date hastenTime = DateUtils.addDay(startTime, taskHastenVO.getDays());

        // 催办时间小于等于当前时间
        if (hastenTime.compareTo(new Date()) <= 0) {
            return;
        }

        List<SysProcessHasten> hastenList = new ArrayList<>();
        optUserList.forEach(item -> {
            SysProcessHasten entity = new SysProcessHasten();
            entity.setId(UUIDUtils.get());
            entity.setProInsId(task.getProcessInstanceId());
            entity.setTaskId(task.getId());
            entity.setHastenUserId(item);
            entity.setUserId(userId);
            entity.setHastenTime(hastenTime);
            entity.setSmsNotice(taskHastenVO.getIsSmsNotice());
            hastenList.add(entity);
        });

        processHastenService.saveBatch(hastenList);
    }

}
